
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.table.DefaultTableModel;
import com.thoughtworks.xstream.XStream;
/**
 * Klasa kontrolera okna głównego. Zawiera funkcje wywoływane po interakcjach użytkownika z aplikacją oraz funkcje wczytujące dane i ustawiające
 * w klasach widoku.
 * @author Krzysztof Marjański, Łukasz Gadomski.
 */
public class Application {

	/**Referencja do obiektu klasy CalendarWindow zawierającym okno główne programu*/
	private CalendarWindow view;
	/**Referencja do obiektu klasy ApplicationModel zawierającym dane i stan aplikacji programu*/
	private ApplicationModel model;
	/**Referencja do obiektu klasy DB umośliwiającym połączenie z bazą*/
	private DB database;
	Reminder rem;
	public ApplicationModel getModel()
	{
		return this.model;
	}
	/**
	 * Alokuje i inicjalizuje obiekt klasy Application. Służy on jako kontroler całego programu. Konstruktor nawiązuje połączenie z bazą oraz 
	 * ustawia początkowe wartości obiektów wykorzystywanych w całym programie na:
	 * Data startowa i końcowa - Aktualna data lub data podana w argumentach wywołania programu.
	 * Filter zdarzeń - pusty ciąg znaków.
	 * Kolor niskiego priorytetu - zielony.
	 * Kolor normalnego priorytetu - czarny.
	 * Kolor wysokiego priorytetu - czerwony.
	 * Przypomnienie włączone.
	 * Dźwięk przypomnienia o ID 1
	 * Aktualny widok na wyświetlanie godzin
	 * @param args
	 * @throws InterruptedException 
	 */
	Application (String[] args) throws InterruptedException
	{
		Calendar c=Calendar.getInstance();
		if (args.length==3)
		{
			//można by ewentualnie sprawdzić czy dane są poprawne
			c.set(Integer.parseInt(args[0]), Integer.parseInt(args[1]), Integer.parseInt(args[2]));
		}
		model = new ApplicationModel(this);
		model.setStartDate(c);
		model.setEndDate(c);
		view = new CalendarWindow(this,model.getStartDate());
		database = new DB();
		view.dayInfo.Update(model.getStartDate(), database.getNameDay(model.getStartDate()),database.getSunrise(model.getStartDate()),
				database.getSunset(model.getStartDate()));
		model.setFilter("");
		model.setHighPriorityColor("red");
		model.setLowPriorityColor("green");
		model.setNormalPriorityColor("black");
		model.setRemindOn(true);
		model.setSoundID(1);
		model.setCurrentView(0);
		refresh();
		rem = new Reminder();
		rem.run(this);

	}
	/**
	 * Zapisuje wszystkie wydarzenia do bazy MySQL.
	 */
	public void toSQL (){database.toSQL(model.eventsList);}
	/**
	 * Pobiera wszystkie wydarzenia z bazy MySQL oraz wywołuje metodę update().
	 */
	public void fromSQL (){model.eventsList.clear();
		model.eventsList.addAll(database.fromSQL());
		refresh();}
	/**
	 * Zapisuje wszystkie wydarzenia do formatu standartowego vCalendar.
	 */
	public void toVCalendar()
	{
		JFileChooser fc = new JFileChooser();
		fc.showSaveDialog(view);
		File f=fc.getSelectedFile();
		if (f==null) return;
		FileWriter out;
		try {
			out = new FileWriter(f);
		
		out.write("BEGIN:VCALENDAR\n");
		out.write("VERSION:2.0\n");
		out.write("PRODID:-//Mroczna Loza Developers//Kalendarz prodzekt v1.0//EN\n");
		for(int i=0;i<this.model.eventsList.size();i++)
		{
			out.write("BEGIN:VEVENT\n");
			out.write(this.model.eventsList.get(i).startDateToICAL());
			out.write(this.model.eventsList.get(i).endDateToICAL());
			out.write("SUMMARY:"+this.model.eventsList.get(i).getName()+"\n");
			out.write("LOCATION:"+this.model.eventsList.get(i).getLocation()+"\n");
			out.write("DESCRIPTION:"+this.model.eventsList.get(i).getDescription()+"\n");
			if(this.model.eventsList.get(i).getRemind()>0)
			{
				out.write("BEGIN:VALARM\n");
				out.write("TRIGGER;VALUE=DURATION;RELATED=START:-PT"+this.model.eventsList.get(i).getRemind()+"M\n");
				out.write("ACTION:DISPLAY\n");
				out.write("END:VALARM\n");
			}
			out.write("END:VEVENT\n");
		}
		out.write("END:VCALENDAR");
		out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * Zapisuje wszystkie wydarzenia do formatu XML.
	 */
	public void toXML()
	{
		JFileChooser fc = new JFileChooser();
		fc.showSaveDialog(view);
		File f=fc.getSelectedFile();
		if (f==null) return;
		XStream xstream = new XStream();
		String xml = xstream.toXML(this.model.eventsList);
		xstream.alias("event", Event.class);
		FileWriter out;
		try {
			out = new FileWriter(f);
			out.write(xml);
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	/**
	 * Pobiera wszystkie wydarzenia z formatu XML oraz wywołuje metodę update().
	 */
	public void fromXML()
	{
		JFileChooser fc = new JFileChooser();
		fc.showOpenDialog(view);
		File file=fc.getSelectedFile();
		if (file==null) return;
        XStream xstream = new XStream();
        ArrayList<Event> list = new ArrayList<Event>();
        xstream.alias("list", java.util.List.class);
        xstream.alias("event", Event.class);
        FileReader in;
		try {
			in = new FileReader(file);
		
        char[] buf = new char[(int)file.length()];
        in.read(buf);
        String buf2 = new String(buf);
        list = (ArrayList<Event>) xstream.fromXML(buf2);
		} catch (FileNotFoundException e) {
			JOptionPane.showMessageDialog(view,"Eggs are not supposed to be green.","Błąd", JOptionPane.ERROR_MESSAGE);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		model.eventsList.clear();
		model.eventsList.addAll(list);
		refresh();
	}
	/**
	 * Uaktualnia aktualnie wybraną datę początkową i końcową oraz wywołuje medodę update().
	 * @param startDate Data początkowa.
	 * @param endDate Data końcowa.
	 */
	public void newDateSelected (Calendar startDate, Calendar endDate)
	{
		model.setStartDate(startDate);
		model.setEndDate(endDate);
		
		view.dayInfo.Update(model.getStartDate(), database.getNameDay(model.getStartDate()),database.getSunrise(model.getStartDate()),
				database.getSunset(model.getStartDate()));
		if (model.getCurrentView()==0)
		view.setModel(createTableModel(startDate,endDate),EventsView.HOUR_VIEW);	
	}
	/**
	 * Uaktualnia filter wydarzeń oraz wywołuje metodę refresh().
	 * @param s Nowy filter.
	 */
	public void newFilter (String s)
	{
		model.setFilter(s);
		refresh();
	}
	/**
	 * Zmienia typ widoku.
	 * @param type Nowy typ.
	 */
	public void setView (int type)
	{
		model.setCurrentView(type);
		refresh();		
	}
	/**
	 * Wywołuje okno ustawień, odczytuje i zapisuje ustawienia.
	 */
	public void settings ()
	{
		SettingsDialog sD = new SettingsDialog (view,"Ustawienia");
		sD.setLowColor(model.getPriorityColor(0));
		sD.setNormalColor(model.getPriorityColor(1));
		sD.setHighColor(model.getPriorityColor(2));
		sD.setRemind(model.isRemindOn());
		sD.setSoundID(model.getSoundID());
		if(sD.exec())
		{
			model.setHighPriorityColor(sD.getHighColor());
			model.setNormalPriorityColor(sD.getNormalColor());
			model.setLowPriorityColor(sD.getLowColor());
			model.setRemindOn(sD.isRemind());
			model.setSoundID(sD.getSoundID());
			refresh();
			//trzeba tu jeszcze zrobić tak aby po wykonaniu tego okienka
			//wszystkie komponenty się zaktualizowały.
		}
	}
	/**
	 * Wywołuje okno dodawania nowego wydarzenia oraz automatycznie ustawia godzinę początkową oraz końcową na podstawie przekazanych parametrów.
	 * @param row Rząd aktualnie zaznaczonej komórki w eventsView.
	 * @param col Kolumna aktualnie zaznaczonej komórki z eventsView.
	 */
	public void addNewEvent(int row, int col)
	{
		Calendar c=computeDate(row,col);
		EventDialog w = new EventDialog (view,"Dodaj nowe wydarzenie");
	    w.setDate(c);
	    if(w.exec())
	    {
	    	model.eventsList.add(w.getFields());
	    	refresh();
	    }
			
	}
	/**
	 * Edytuje wydarzenie. Ładuje wszystkie wydarzenia, które są umieszczone w komórce o przekazanych współrzędnych do okna dialogowego
	 * po czym je wyświetla i zapisuje ewentualne zmiany.
	 * @param row Rząd aktualnie zaznaczonej komórki w eventsView.
	 * @param col Kolumna aktualnie zaznaczonej komórki w eventsView.
	 */
	public void editEvent (int row, int col)
	{
		Calendar c=computeDate(row,col);
		EventDialog w = new EventDialog (view,"Edytuj wydarzenie");
		if ((model.getCurrentView()!=0)) w.addEvent(model.eventsList.get(row), row);
		else
		{
			for (int i = 0 ; i<model.eventsList.size() ; i++)
			{
				if (c.equals(model.eventsList.get(i).getEventStart()))
				{
					w.addEvent(model.eventsList.get(i), i);
				}
			}
			
		}
		if(w.exec())
		{
			model.eventsList.remove(w.getEditedIndex());
		    model.eventsList.add(w.getFields());
		    refresh();
		}
		
		
	}
	/**
	 * Usuwa wydarzenia. Ładuje wszystkie wydarzenia, które są umieszczone w komórce o przekazanych współrzędnych do okna dialogowego.
	 * Wyświetla je i zapisuje ewentualne zmiany.
	 * @param row Rząd aktualnie zaznaczonej komórki w eventsView.
	 * @param col Kolumna aktualnie zaznaczonej komórki w eventsView.
	 */
	public void removeEvent (int row, int col)
	{
		Calendar c=computeDate(row,col);
		DeleteDialog dDialog = new DeleteDialog (view,"Usuń wydarzenie");
		if (model.getCurrentView()!=0) dDialog.addEvent(model.eventsList.get(row).getName(), row);
		else{
			for (int i = 0 ; i<model.eventsList.size() ; i++)
			{
				if (c.equals(model.eventsList.get(i).getEventStart()))
				{
					dDialog.addEvent(model.eventsList.get(i).getName(), i);
				
				}
			}
		}
		if(dDialog.exec())
		{
			model.eventsList.remove(dDialog.getIndexToRemove());
			refresh();
		}
	}
	/**
	 * Wyświetla okno dialogowe z zapytaniem po podanie daty od której wcześniejsze wydarzenia będą usunięte.
	 */
	void removeOld ()
	{
		DeleteOldDialog dod = new DeleteOldDialog (view,"Usuń stare wpisy...");
		if (dod.exec())
		{
			for (int i = 0 ; i<model.eventsList.size() ; i++)
			{
				if(model.eventsList.get(i).getEventStart().before(dod.getDate()))
						{
							model.eventsList.remove(i);
							i--;
						}
			}
			refresh();
		}
	}
	private Calendar computeDate (int row, int col)
	{
		Calendar c=(Calendar)model.getStartDate().clone();
		
		c.set(Calendar.SECOND,0);
		c.set(Calendar.MILLISECOND, 0);
		c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH)+col-1);
		c.set(Calendar.MINUTE, (30*row)%60);
		c.set(Calendar.HOUR_OF_DAY, (row/2));
		return c;
	}
	/**
	 * Przeładowuje odpowiedni model  do widoku. Wywoływane po wprowadzeniu nowych danych.
	 */
	protected void refresh ()
	{
		if (model.getCurrentView()==0)
			view.setModel(createTableModel(model.getStartDate(),model.getEndDate()),EventsView.HOUR_VIEW);
		if (model.getCurrentView()==1)
			view.setModel(createSortedTableModel2(), EventsView.SORT_VIEW);
		if (model.getCurrentView()==2)
			view.setModel(createSortedTableModel(), EventsView.SORT_VIEW);
		if (model.getCurrentView()==3)
			view.setModel(createSortedTableModel3(), EventsView.SORT_VIEW);	
	}
	
	/**
	 * Wybiera dane z modelu oraz tworzy z nich obiekt klasy DefaultTableModel z zablokowaną możliwością edycji.
	 * @param startDate Początkowa data od ktorej mają być wybierane wydarzenia.
	 * @param endDate Końcowa data do której mają być wybierane wydarzenia.
	 * @return Przygotowany do załadowania do EventsView model tabeli.
	 */
	protected DefaultTableModel createTableModel (Calendar startDate, Calendar endDate)
	{
		DefaultTableModel tmodel;
		int k=0;
		int priority=0;
		Calendar tmpDate = (Calendar)startDate.clone(); //data pomocnicza
		tmpDate.set(Calendar.HOUR_OF_DAY,0);
		tmpDate.set(Calendar.MINUTE,0);
		tmpDate.set(Calendar.SECOND,0);
		tmpDate.set(Calendar.MILLISECOND, 0);
		int y = (endDate.get(Calendar.DATE) - startDate.get(Calendar.DATE))+1;	//obliczam ile dni zostalo zaznaczonych
		int startDay = startDate.get(Calendar.DAY_OF_WEEK)-1;	//indeks dla tablicy z dniami tygodnia
		String [] dayOfWeek = {"Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"};
		String [] month = {"Stycznia","Lutego","Marca","Kwietnia","Maja","Czerwca","Lipca","Sierpnia","Września","Października","Listopada","Grudnia"};
		String [][] data = new String[48][y];
		String [] col = new String[y];
		for(int i=0;i<48;i++)
		{
			for(int j=0;j<y;j++)
			data[i][j]="";
		}
		for(int i=0;i<y;i++)
		{
			tmpDate.set(Calendar.DATE, startDate.get(Calendar.DATE)+i);
			if(startDay==7)
			startDay=0;
			col[i]=dayOfWeek[startDay] + " " + tmpDate.get(Calendar.DATE) + " " + month[startDate.get(Calendar.MONTH)];		//podpisanie kolumn nazwa wybranego dnia
			startDay++;
			
			for(int j=0;j<model.eventsList.size();j++)
			{
				if (model.eventsList.get(j).getName().toLowerCase().startsWith(model.getFilter().toLowerCase()))
				{
					if(tmpDate.equals(model.eventsList.get(j).eventStartDate())) //sprawdzam czy w danym dniu zaczyna sie jakis event
					{
						k=model.eventsList.get(j).convertStartHour();	//obliczam indeks (godzina w tabeli) 
						priority=model.eventsList.get(j).getPriority();
					
						data[k][i]+="<html><font color="+model.getPriorityColor(priority)+">"+model.eventsList.get(j).getName()+"</font><br><html>"; //zapisuje w komorce informacje na temat eventu
				
					}
					if(tmpDate.equals(model.eventsList.get(j).eventEndDate()))//sprawdzam czy w danym dniu konczy sie jakis event
					{
						k=model.eventsList.get(j).convertEndHour();    //obliczam indeks(godzina w tabeli)
						priority=model.eventsList.get(j).getPriority();
					
						data[k][i]+="<html><font color="+model.getPriorityColor(priority)+">Koniec wydarzenia: "+model.eventsList.get(j).getName()+"</font><br><html>"; //zapisuje w komorce informacje na temat eventu
					
					}
				}
			}
		}
		tmodel = new DefaultTableModel (data,col){public boolean isCellEditable(int rowIndex, int columnIndex){return false;}  };
		return tmodel;
	}
	/**
	 * Tworzy posortowany po lokacji model tabeli.
	 * @return Przygotowany do załadowania do EventsView model tabeli.
	 */
	protected DefaultTableModel createSortedTableModel()
	{
		DefaultTableModel tmodel;
		int priority;
		Collections.sort(model.eventsList);
		String data [][] = new String [model.eventsList.size()][1];
		String col [] = {"Wydarzenia posortowane według lokacji"};
		for(int i=0;i<model.eventsList.size();i++)
		{
			priority=model.eventsList.get(i).getPriority();
			data[i][0]="<html><font face=Arial color="+model.getPriorityColor(priority)+">"+model.eventsList.get(i).getName()+"</font><br><html>"; //zapisuje w komorce informacje na temat eventu
		}
		tmodel = new DefaultTableModel(data,col){public boolean isCellEditable(int rowIndex, int columnIndex){return false;}  };
		return tmodel;
		
	}
	/**
	 * Tworzy posortowany po nazwie model tabeli
	 * @return Przygotowany do załadowania do EventsView model tabeli.
	 */
	private DefaultTableModel createSortedTableModel2()
	{
		DefaultTableModel tmodel;
		int priority;
		Collections.sort(model.eventsList,new CompareName());
		String data [][] = new String [model.eventsList.size()][1];
		String col [] = {"Wydarzenia posortowane według nazwy"};
		for(int i=0;i<model.eventsList.size();i++)
		{
			priority=model.eventsList.get(i).getPriority();
			data[i][0]="<html><font color="+model.getPriorityColor(priority)+">"+model.eventsList.get(i).getName()+"</font><br><html>"; //zapisuje w komorce informacje na temat eventu
		}
		tmodel = new DefaultTableModel(data,col){public boolean isCellEditable(int rowIndex, int columnIndex){return false;}  };
		return tmodel;
		
	}
	}
	





